-
-
Notifications
You must be signed in to change notification settings - Fork 109
fix(gemini): preserve thought signatures for Gemini 3.0 compatibility #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix(gemini): preserve thought signatures for Gemini 3.0 compatibility #218
Conversation
Fixes TanStack#216 Gemini 3.0 models require thought signatures to be preserved and sent back during function calling. When the model returns a functionCall with a thoughtSignature, it must be included in subsequent API requests or the API returns a 400 validation error. Changes: - Add optional `metadata` field to `ToolCall` type for provider-specific data - Update `ToolCallStreamChunk` to use the `ToolCall` interface - Capture thoughtSignature from Gemini responses in stream processing - Include thoughtSignature when formatting messages back to Gemini API This fix ensures compatibility with Gemini 3.0 Flash and other Gemini 3 models that enforce thought signature validation for tool calls.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... 📒 Files selected for processing (1)
✏️ Tip: You can disable in-progress messages by setting Tip You can customize the tone of the review comments and chat replies.Set the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughThe changes add Gemini 3.0 compatibility by introducing a metadata field to ToolCall and implementing thoughtSignature propagation. The text adapter now captures the thoughtSignature from tool call parts and includes it in emitted tool_call metadata, while ToolCallStreamChunk is standardized to directly reference ToolCall instead of inlining the object structure. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/typescript/ai-gemini/src/adapters/text.ts (1)
333-354: Consistent thoughtSignature handling for UNEXPECTED_TOOL_CALL.The same thoughtSignature capture pattern is correctly applied when handling
UNEXPECTED_TOOL_CALLfinish reasons, ensuring consistent metadata propagation across all tool call scenarios.Optional: Extract helper to reduce duplication
The thoughtSignature capture logic (lines 274-278 and 333-337) could be extracted to a small helper function:
private captureThoughtSignature(part: Part): { thoughtSignature: string } | undefined { return 'thoughtSignature' in part && part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : undefined }Then use:
const metadata = this.captureThoughtSignature(part)This is optional given the simplicity and limited scope of the duplication.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/typescript/ai-gemini/src/adapters/text.tspackages/typescript/ai/src/types.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from/adapterssubpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions withtoolDefinition()and Zod schema inference
Implement isomorphic tool system usingtoolDefinition()with.server()and.client()implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses
Files:
packages/typescript/ai/src/types.tspackages/typescript/ai-gemini/src/adapters/text.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for function and variable names throughout the codebase
Files:
packages/typescript/ai/src/types.tspackages/typescript/ai-gemini/src/adapters/text.ts
packages/typescript/*/src/adapters/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking
Files:
packages/typescript/ai-gemini/src/adapters/text.ts
🧠 Learnings (4)
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/model-meta.ts : Maintain model metadata files that define provider options and capabilities per model for per-model type safety
Applied to files:
packages/typescript/ai/src/types.tspackages/typescript/ai-gemini/src/adapters/text.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses
Applied to files:
packages/typescript/ai/src/types.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking
Applied to files:
packages/typescript/ai-gemini/src/adapters/text.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size
Applied to files:
packages/typescript/ai-gemini/src/adapters/text.ts
🧬 Code graph analysis (1)
packages/typescript/ai/src/types.ts (1)
packages/typescript/ai-devtools/src/store/ai-context.tsx (1)
ToolCall(17-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Agent
🔇 Additional comments (4)
packages/typescript/ai/src/types.ts (2)
94-99: LGTM! Well-documented provider metadata field.The optional
metadatafield withunknowntype provides the necessary flexibility for adapters to attach provider-specific data like Gemini'sthoughtSignature. The documentation clearly explains the purpose and usage.
684-684: LGTM! Improved type consistency.Standardizing
ToolCallStreamChunkto directly reference theToolCallinterface eliminates type duplication and ensures automatic consistency when theToolCallinterface is extended (like the newmetadatafield).packages/typescript/ai-gemini/src/adapters/text.ts (2)
478-495: LGTM! Completes thoughtSignature round-trip for Gemini 3.0.The code correctly extracts
thoughtSignaturefromtoolCall.metadataand injects it back into the Part object when formatting messages for Gemini. The type guards are thorough, and theas anycast is necessary since the Part type definition from@google/genaidoesn't includethoughtSignaturedespite the API requiring it.This completes the round-trip: capture from Gemini response → store in metadata → inject back to Gemini request, ensuring Gemini 3.0 validation passes.
274-292: Correct implementation of required Gemini 3.0 thoughtSignature handling.The code properly captures
thoughtSignaturefrom function call parts and stores it in the emitted tool call's metadata field. This is critical for Gemini 3.0 API compatibility—thought signatures are strictly enforced and must be preserved exactly and returned in follow-up requests to avoid 400 errors. The implementation correctly uses existence checks and properly integrates with theToolCall.metadatafield, which is documented as supporting provider-specific metadata for this purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for Gemini 3.0 thought signatures in function calling. Gemini 3.0 models require thought signatures to be preserved and sent back during function calling to avoid API validation errors.
Changes:
- Added optional
metadatafield toToolCalltype for provider-specific data storage - Updated
ToolCallStreamChunkto reuse theToolCallinterface instead of inline type definition - Implemented thought signature capture from Gemini API responses and preservation when formatting messages
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| packages/typescript/ai/src/types.ts | Added optional metadata field to ToolCall interface; refactored ToolCallStreamChunk to use ToolCall type |
| packages/typescript/ai-gemini/src/adapters/text.ts | Captured thought signatures from streaming responses; preserved thought signatures when formatting tool calls back to API |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 'thoughtSignature' in toolCall.metadata && | ||
| typeof toolCall.metadata.thoughtSignature === 'string' | ||
| ) { | ||
| ;(part as any).thoughtSignature = toolCall.metadata.thoughtSignature |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a type assertion to any to bypass TypeScript's type checking when assigning thoughtSignature. This makes the code less type-safe and harder to maintain. Consider defining a more specific type or interface that includes the thoughtSignature property, or use a type-safe approach to add the property.
| // Capture thought signature for Gemini 3.0 compatibility | ||
| const metadata = | ||
| 'thoughtSignature' in part && part.thoughtSignature | ||
| ? { thoughtSignature: part.thoughtSignature } | ||
| : undefined |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for capturing thoughtSignature from parts is duplicated in multiple locations (lines 274-278, 333-337, and the similar pattern at line 485-492). Consider extracting this into a helper function to reduce duplication and improve maintainability. For example, a function like extractThoughtSignatureMetadata(part) could be reused across all these locations.
| 'thoughtSignature' in part && part.thoughtSignature | ||
| ? { thoughtSignature: part.thoughtSignature } |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code accesses part.thoughtSignature without a type assertion, which will cause a TypeScript compilation error if thoughtSignature is not a property of the Part type from @google/genai. While the runtime check using the in operator is correct, TypeScript doesn't narrow the type based on this check. Consider using a type assertion like (part as any).thoughtSignature consistently, or define a type guard function that properly narrows the type.
| 'thoughtSignature' in part && part.thoughtSignature | |
| ? { thoughtSignature: part.thoughtSignature } | |
| 'thoughtSignature' in (part as any) && (part as any).thoughtSignature | |
| ? { thoughtSignature: (part as any).thoughtSignature } |
| 'thoughtSignature' in part && part.thoughtSignature | ||
| ? { thoughtSignature: part.thoughtSignature } |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code accesses part.thoughtSignature without a type assertion, which will cause a TypeScript compilation error if thoughtSignature is not a property of the Part type from @google/genai. While the runtime check using the in operator is correct, TypeScript doesn't narrow the type based on this check. Consider using a type assertion like (part as any).thoughtSignature consistently, or define a type guard function that properly narrows the type.
| 'thoughtSignature' in part && part.thoughtSignature | |
| ? { thoughtSignature: part.thoughtSignature } | |
| 'thoughtSignature' in part && (part as any).thoughtSignature | |
| ? { thoughtSignature: (part as any).thoughtSignature } |
| // Capture thought signature for Gemini 3.0 compatibility | ||
| const metadata = | ||
| 'thoughtSignature' in part && part.thoughtSignature | ||
| ? { thoughtSignature: part.thoughtSignature } | ||
| : undefined |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new thought signature capture and preservation logic is not covered by tests. Consider adding test cases that verify:
- Thought signatures are correctly captured from Gemini responses when present
- The metadata field is properly populated in ToolCall objects
- Thought signatures are correctly included when formatting messages back to the Gemini API
- The behavior is correct when thoughtSignature is absent
This would ensure the Gemini 3.0 compatibility fix works as expected and prevent regressions.
* Initial plan * Add comprehensive test for Gemini thought signature preservation Co-authored-by: amacsmith <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: amacsmith <[email protected]>
Fixes #216
Gemini 3.0 models require thought signatures to be preserved and sent back during function calling. When the model returns a functionCall with a thoughtSignature, it must be included in subsequent API requests or the API returns a 400 validation error.
Changes:
metadatafield toToolCalltype for provider-specific dataToolCallStreamChunkto use theToolCallinterfaceThis fix ensures compatibility with Gemini 3.0 Flash and other Gemini 3 models that enforce thought signature validation for tool calls.
🎯 Changes
✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.